/*
 *  Copyright (c) 2018, Infineon Technologies AG
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification,are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *  - Neither the name of the copyright holders nor the names of its contributors
 *  may be used to endorse or promote products derived from this software without
 *  specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY,OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 *  To improve the quality of the software, users are encouraged to share
 *  modifications, enhancements or bug fixes with Infineon Technologies AG
 *  dave@infineon.com).
 */
/**
 * \defgroup PWM Pulse Width Modulation
 * @{
 */
/**
 * \file	config_pwm.c
 * \author 	Manuel Escudero Rodriguez
 * \date	09.05.2018
 * \brief   Pulse Width Modulation
 */
#include <DAVE.h>
#include "xmc4200.h"
#include "config_pwm.h"
#include "config_adc.h"
#include "controller.h"

/** Initialization data of the CSG0 unit. Linked to PWM_C (CCU80.2) */
const XMC_HRPWM_CSG_CONFIG_t csg0_init_config =
{
    .cmp_config =
    {
        .ibs = XMC_HRPWM_CSG_INPUT_SEL_IG, /* Blanking start trigger from CCU40.ST0 (PWM_BLK_CSG0) */
        .cmp_input_sel = XMC_HRPWM_CSG_CMP_INPUT_CINA, /* Comparator input pin selection */
        .cmp_input_sw = XMC_HRPWM_CSG_LVL_SEL_DISABLED, /* Comparator input switching configuration */
        .cmp_ext_sw_enable = XMC_HRPWM_FUNC_STATUS_DISABLE, /* Disable switching of input via external trigger. */
        .cmp_out_inv = XMC_HRPWM_FUNC_STATUS_DISABLE, /* Comparator output inversion disable */
        .blanking_mode = XMC_HRPWM_CSG_EDGE_SEL_FALLING_EDGE, /* Blanking starting trigger */
        .blank_ext_enable = XMC_HRPWM_FUNC_STATUS_ENABLE, /* Blanking via external trigger enable */
        .blanking_val = CSG_BLK, /* Blanking value. Blanking time = blanking_val * module clk freq */
        .filter_enable = XMC_HRPWM_FUNC_STATUS_DISABLE, /* Comparator output filter disable */
        .filter_window = XMC_HRPWM_CSG_CMP_FILTER_WINDOW_2_CLK_CYCLES, /* Comparator output filter window cycles */
        .filter_control = 0, /* Comparator output filter control */
		.ipls = XMC_HRPWM_CSG_INPUT_SEL_IG, /* Clamping input from CCU80.ST3 (PWM_A_B) */
        .clamp_ctrl_lvl = XMC_HRPWM_CSG_LVL_SEL_HIGH, /* Clamping control signal level selection */
        .clamp_level = 0, /* Output passive level value */
        .clamp_exit_sw_config = 0, /* Clamped state exit software configuration */
        .clamp_enter_config = 0, /* Clamping level enter configuration */
        .clamp_exit_config = 0 /* Clamping level exit configuration */
    },
    .dac_config =
    {
        /* DSV1 is used as initial value and initial conversion trigger is generated */
        .start_mode = XMC_HRPWM_CSG_SWSM_DSV1_W_TRIGGER,
        .dac_dsv1 = 0,
        .dac_dsv2 = 0
    },
    .sgen_config =
    {
        /* Start of CSGySC resgister definition --> Slope Control */
        .ctrl_mode = XMC_HRPWM_CSG_SLOPE_CTRL_MODE_DEC_GEN, /* Slope generation mode */
        .prescaler_ext_start_mode = XMC_HRPWM_CSG_PRESCALER_EXT_START_CLR_N_STRT,
        .prescaler_ext_stop_mode = XMC_HRPWM_CSG_PRESCALER_EXT_STOP_STP,
        .fixed_prescaler_enable = 1, /* Fixed prescaler (division by 4), 0:enabled, 1:disabled */
        .prescaler = XMC_HRPWM_CSG_PRESCALER_DIVISION_BY_1, /* Prescaler division. */
        .ext_start_mode = XMC_HRPWM_CSG_SLOPE_EXT_START_STRT, /* Slope generation on external start trigger. */
        .ext_stop_mode = XMC_HRPWM_CSG_SLOPE_EXT_STOP_STP, /* Slope generation on external stop trigger.*/
        .slope_ref_val_mode = 0U, /* Only DSV1 is used for slope generation */
        .step_gain = XMC_HRPWM_CSG_SLOPE_STEP_GAIN_INC_DEC_BY_2,
        .static_mode_ist_enable = XMC_HRPWM_FUNC_STATUS_DISABLE, /* Function (immediate shadow transfer) is disabled */
        .pulse_swallow_enable = XMC_HRPWM_FUNC_STATUS_ENABLE,
        .pulse_swallow_win_mode = 0U, /* Window for pulse swallow */
        .pulse_swallow_val = 7, /* Swallow cycles per window  */
    }
};
/** Initialization data of the slope generation trigger. */
const XMC_HRPWM_CSG_INPUT_CONFIG_t sg0_input_config =
{
    .mapped_input = XMC_HRPWM_CSG_INPUT_SEL_IF, /* Starts of slope generation from CCU80.ST3 (PWM_A_B) */
    .edge = XMC_HRPWM_CSG_EDGE_SEL_RISING_EDGE, /* Active edge of mapped_input */
    .level = XMC_HRPWM_CSG_LVL_SEL_DISABLED, /* Active level of mapped_input */
};
/** Initialization data of the CSG2 unit. Linked to PWM_D (CCU80.3) */
const XMC_HRPWM_CSG_CONFIG_t csg2_init_config =
{
    .cmp_config =
    {
        .ibs = XMC_HRPWM_CSG_INPUT_SEL_IF, /* Blanking start trigger from CCU80.ST3 (PWM_A_B) */
        .cmp_input_sel = XMC_HRPWM_CSG_CMP_INPUT_CINA, /* Comparator input pin selection */
        .cmp_input_sw = XMC_HRPWM_CSG_LVL_SEL_DISABLED, /* Comparator input switching configuration */
        .cmp_ext_sw_enable = XMC_HRPWM_FUNC_STATUS_DISABLE, /* Enable switching of input via external trigger. */
        .cmp_out_inv = XMC_HRPWM_FUNC_STATUS_DISABLE, /* Comparator output inversion disable*/
        .blanking_mode = XMC_HRPWM_CSG_EDGE_SEL_FALLING_EDGE, /* Blanking mode */
        .blank_ext_enable = XMC_HRPWM_FUNC_STATUS_ENABLE, /* Blanking via external trigger enable */
        .blanking_val = CSG_BLK, /* Blanking value. Blanking time = blanking_val*module clk freq */
        .filter_enable = XMC_HRPWM_FUNC_STATUS_DISABLE, /* Comparator output filter. */
        .filter_window = XMC_HRPWM_CSG_CMP_FILTER_WINDOW_2_CLK_CYCLES, /* Comparator output filter window cycles */
        .filter_control = 0, /* Comparator output filter control */
		.ipls = XMC_HRPWM_CSG_INPUT_SEL_IF, /* Clamping input from CCU80.ST3 (PWM_A_B) */
        .clamp_ctrl_lvl = XMC_HRPWM_CSG_LVL_SEL_HIGH, /* Clamping control signal level selection */
        .clamp_level = 0, /* Output passive level value */
        .clamp_exit_sw_config = 0, /* Clamped state exit software configuration */
        .clamp_enter_config = 0, /* Clamping level enter configuration */
        .clamp_exit_config = 0 /* Clamping level exit configuration */
    },
    .dac_config =
    {
        /* DSV1 is used as initial value and initial conversion trigger is generated */
        .start_mode = XMC_HRPWM_CSG_SWSM_DSV1_W_TRIGGER,
        .dac_dsv1 = 0,
        .dac_dsv2 = 0
    },
    .sgen_config =
    {
        /* Start of CSGySC resgister definition --> Slope Control */
        .ctrl_mode = XMC_HRPWM_CSG_SLOPE_CTRL_MODE_DEC_GEN, /* Slope generation mode */
        .prescaler_ext_start_mode = XMC_HRPWM_CSG_PRESCALER_EXT_START_CLR_N_STRT, /* Prescaler operation on external start trigger */
        .prescaler_ext_stop_mode = XMC_HRPWM_CSG_PRESCALER_EXT_STOP_STP, /* Prescaler operation on external stop trigger */
        .fixed_prescaler_enable = 1, /* Fixed pre-scaler (division by 4), 0:enabled, 1:disabled */
        .prescaler = XMC_HRPWM_CSG_PRESCALER_DIVISION_BY_1, /* Prescaler division. */
        .ext_start_mode = XMC_HRPWM_CSG_SLOPE_EXT_START_STRT, /* Slope generation on external start trigger */
        .ext_stop_mode = XMC_HRPWM_CSG_SLOPE_EXT_STOP_STP, /* Slope generation on external stop trigger */
        .slope_ref_val_mode = 0U, /* Only DSV1 is used for slope generation */
        .step_gain = XMC_HRPWM_CSG_SLOPE_STEP_GAIN_INC_DEC_BY_2,
        .static_mode_ist_enable = XMC_HRPWM_FUNC_STATUS_DISABLE, /* Function (immediate shadow transfer) is disabled */
        .pulse_swallow_enable = XMC_HRPWM_FUNC_STATUS_ENABLE,
        .pulse_swallow_win_mode = 0U, /* Window for pulse swallow */
        .pulse_swallow_val = 7, /* Swallow cycles per window  */
    }
};
/** Initialization data of the slope generation trigger. */
const XMC_HRPWM_CSG_INPUT_CONFIG_t sg2_input_config =
{
    .mapped_input = XMC_HRPWM_CSG_INPUT_SEL_IF, /* Starts of slope generation from CCU80.ST3 (PWM_A_B) */
    .edge = XMC_HRPWM_CSG_EDGE_SEL_FALLING_EDGE, /* Active edge of mapped_input */
    .level = XMC_HRPWM_CSG_LVL_SEL_DISABLED, /* Active level of mapped_input */
};

/*---------------------------------------------------------------------------*/
/**
 * \brief   PWM configuration
 * \return  None
 *
 * Overrides some of the configurations done by DAVE, connects and configures the timer slices.
 */
void pwm_init()
{
	/* Set the compare value of burst counter */
	XMC_CCU4_SLICE_SetTimerCompareMatch(BURST_CNT.ccu4_handle->slice_ptr, parameters_RAM.burst_lenght);
	XMC_CCU4_SLICE_SetTimerPeriodMatch(BURST_CNT.ccu4_handle->slice_ptr, parameters_RAM.burst_lenght + 1);
	XMC_CCU4_EnableShadowTransfer(BURST_CNT.ccu4_handle->global_handle->module_ptr, BURST_CNT.ccu4_handle->shadow_mask);

	/*
     * CSG0 configuration. Linked to PWM_C (CCU80.2)
     */
    /* CSG slice initialization */
    XMC_HRPWM_CSG_Init(PWM_CSG_C, &csg0_init_config);
    XMC_HRPWM_CSG_StartSlopeGenConfig(PWM_CSG_C, &sg0_input_config);
    XMC_HRPWM_EnableComparatorShadowTransfer(HRPWM0, XMC_HRPWM_SHADOW_TX_DAC0);
    /* Start HRPWM CSG, DAC and Comparator*/
    XMC_HRPWM_StartDac(HRPWM0, XMC_HRPWM_CSG_RUN_BIT_DAC0);
    XMC_HRPWM_StartComparator(HRPWM0, XMC_HRPWM_CSG_RUN_BIT_CMP0);
    XMC_HRPWM_StartSlopeGeneration(HRPWM0, (XMC_HRPWM_CSG_SLOPE_START_DAC0 | XMC_HRPWM_CSG_PRESCALER_START_CSG0));
    
    /*
     * CSG2 configuration. Linked to PWM_D (CCU80.3)
     */
    /* CSG slice initialization */
    XMC_HRPWM_CSG_Init(PWM_CSG_D, &csg2_init_config);
    XMC_HRPWM_CSG_StartSlopeGenConfig(PWM_CSG_D, &sg2_input_config);
    XMC_HRPWM_EnableComparatorShadowTransfer(HRPWM0, XMC_HRPWM_SHADOW_TX_DAC2);
    /* Start HRPWM CSG, DAC and Comparator*/
    XMC_HRPWM_StartDac(HRPWM0, XMC_HRPWM_CSG_RUN_BIT_DAC2);
    XMC_HRPWM_StartComparator(HRPWM0, XMC_HRPWM_CSG_RUN_BIT_CMP2);
    XMC_HRPWM_StartSlopeGeneration(HRPWM0,(XMC_HRPWM_CSG_SLOPE_START_DAC2 | XMC_HRPWM_CSG_PRESCALER_START_CSG2));
    
    /* Connect inputs of ERUs from comparators and from PWM signals */
    /* Reset D ERU from comparator 2 and status of PWM A. Status of PWMA is associated with the CR2 value, this way the shift is adjusted. */
    XMC_ERU_ETL_SetInput(XMC_ERU1, 3, (uint32_t)XMC_ERU_ETL_INPUT_A0, (uint32_t)XMC_ERU_ETL_INPUT_B3);
    /* Reset C ERU from comparator 0 and pin 1.3 (B output) */
    XMC_ERU_ETL_SetInput(XMC_ERU1, 2, (uint32_t)XMC_ERU_ETL_INPUT_A2, (uint32_t)XMC_ERU_ETL_INPUT_B3);

    /* Input external start from PWM D falling edge */
    PWM_C.ccu8_slice_ptr->INS &= ~(((uint32_t) XMC_CCU8_SLICE_EVENT_INPUT_CONFIG_MASK) << ((uint8_t) CCU8_CC8_INS_EV0IS_Pos));
    PWM_C.ccu8_slice_ptr->INS |= ((uint32_t) XMC_CCU8_SLICE_INPUT_N) << ((uint8_t) CCU8_CC8_INS_EV0IS_Pos);

    /* Set status output of PWM_A_B associated to CR2 and sets a value for this comparison. This determines the shift of the leading leg */
    XMC_CCU8_SLICE_ConfigureStatusBitOutput(PWM_A_B.ccu8_slice_ptr, XMC_CCU8_SLICE_STATUS_CHANNEL_1);

    /* Sets trigger signal connected to the ADC queue and the control interrupt. */
    XMC_CCU8_SLICE_EnableEvent(PWM_A_B.ccu8_slice_ptr, XMC_CCU8_SLICE_IRQ_ID_COMPARE_MATCH_UP_CH_1);
    XMC_CCU8_SLICE_SetInterruptNode(PWM_A_B.ccu8_slice_ptr, XMC_CCU8_SLICE_IRQ_ID_COMPARE_MATCH_UP_CH_1, XMC_CCU8_SLICE_SR_ID_2);

    /*
     * PWM A B second status output, this is linked to CR2 of PWM_A_B timer and triggers PWM_D stop signal.
     * There was no other way to connect this signal.
     * Triggers also blanking of CSG0 timer.
     */
    XMC_GPIO_SetMode(XMC_GPIO_PORT1, 4, XMC_GPIO_MODE_OUTPUT_PUSH_PULL_ALT3);
    XMC_GPIO_SetMode(XMC_GPIO_PORT2, 8, XMC_GPIO_MODE_OUTPUT_PUSH_PULL_ALT3);
    /* Set the third output as inverted */
    PWM_A_B.ccu8_slice_ptr->CHC &= ~0x10;
    PWM_A_B.ccu8_slice_ptr->CHC |= 0x08;
    
    /* Set the third output as inverted. Trigger of burst counter. */
    XMC_GPIO_SetMode(XMC_GPIO_PORT2, 6, XMC_GPIO_MODE_OUTPUT_PUSH_PULL_ALT3);

    /* Input external start from HR0.Q1 -- C */
    PWM_E.ccu4_slice_ptr->INS &= ~(((uint32_t) XMC_CCU4_SLICE_EVENT_INPUT_CONFIG_MASK) << ((uint8_t) CCU4_CC4_INS_EV0IS_Pos));
    PWM_E.ccu4_slice_ptr->INS &= ~((uint32_t) CCU4_CC4_INS_EV0EM_Msk);
    PWM_E.ccu4_slice_ptr->INS |= ((uint32_t) XMC_CCU4_SLICE_INPUT_F) << ((uint8_t) CCU4_CC4_INS_EV0IS_Pos);
    PWM_E.ccu4_slice_ptr->INS |= ((uint32_t) XMC_CCU4_SLICE_EVENT_EDGE_SENSITIVITY_FALLING_EDGE << (uint32_t) CCU4_CC4_INS_EV0EM_Pos);
    /* Input external start from HR0.Q3 -- D */
    PWM_F.ccu4_slice_ptr->INS &= ~(((uint32_t) XMC_CCU4_SLICE_EVENT_INPUT_CONFIG_MASK) << ((uint8_t) CCU4_CC4_INS_EV0IS_Pos));
    PWM_F.ccu4_slice_ptr->INS &= ~((uint32_t) CCU4_CC4_INS_EV0EM_Msk);
    PWM_F.ccu4_slice_ptr->INS |= ((uint32_t) XMC_CCU4_SLICE_INPUT_F) << ((uint8_t) CCU4_CC4_INS_EV0IS_Pos);
    PWM_F.ccu4_slice_ptr->INS |= ((uint32_t) XMC_CCU4_SLICE_EVENT_EDGE_SENSITIVITY_FALLING_EDGE << (uint32_t) CCU4_CC4_INS_EV0EM_Pos);
    
    /* Blanking trigger for CSG0. */
    PWM_BLK_CSG0.ccu4_slice_ptr->CRS = 0;
    PWM_BLK_CSG0.ccu4_slice_ptr->PRS = PWM_A_B_DUTY - 6;
    
    /** \todo Check if this is needed for the peak current control. It was -5 in the other mode */
    PWM_B_RESET_C.ccu4_slice_ptr->CRS = PWM_A_B_DUTY - 4;
    PWM_B_RESET_C.ccu4_slice_ptr->PRS = PWM_A_B_DUTY;
    PWM_BLK_CSG0.ccu4_module_ptr->GCSS = PWM_BLK_CSG0.shadow_txfr_msk | PWM_B_RESET_C.shadow_txfr_msk;

    /* Set PWM signals trapped before starting them. */
    TRAP_SET();
    /* Adjust dead times to a default value. */
    pwm_dt_set();
    /* Set new DAC start value. */
    peak_ref_set((uint16_t)0);
    
    /* 
     * Setting of switching frequency of the PWM. 
     */
    PWM_A_B.ccu8_slice_ptr->PRS = (uint16_t) PWM_A_B_PERIOD;
    PWM_A_B.ccu8_slice_ptr->CR1S = (uint16_t) PWM_A_B_DUTY;
    PWM_C.ccu8_slice_ptr->PRS = (uint16_t) PWM_D_PERIOD;
    PWM_D.ccu8_slice_ptr->PRS = (uint16_t) PWM_D_PERIOD;
    
    /*
     * Comparison value 2 of the PWM_A_B triggers the stop event of the slice PWM_D.
     * Stop of PWM_D triggers start of PWM_C. This way the shift is adjusted by PWM_A_B means.
     */
	PWM_A_B.ccu8_slice_ptr->CR2S = (uint16_t) MAX_ADJ_PHASE;
    PWM_A_B.ccu8_module_ptr->GCSS = PWM_A_B.shadow_txfr_msk | PWM_C.shadow_txfr_msk | PWM_D.shadow_txfr_msk;
    /* Starts the fan at the lowest speed. */
    fan_duty_set(FAN_PWM_MAX);
}
/*---------------------------------------------------------------------------*/
/**
 * \brief   PWM generation start
 * \return  None
 *
 * Starts generation of PWM signals. Note that if signals have been previously trapped they would not be
 * output to the microcontroller pins.
 */
void pwm_start()
{
	/*
	 * Note: the order of starting the timers matters, as PWM_D and PWM_C are starting
	 * each other with their falling edges. Handle with care! if modified.
	 */
    COUNTER_Start(&FAN_CNT);
    PWM_CCU8_Start(&PWM_FAN);
    PWM_CCU4_Start(&PWM_BLK_CSG0);
    PWM_CCU4_Start(&PWM_B_RESET_C);
    HRPWM_Start(&PWM_D);
    HRPWM_Start(&PWM_C);
    PWM_CCU4_Start(&PWM_E);
    PWM_CCU4_Start(&PWM_F);
    HRPWM_Start(&PWM_A_B);
    /* Synchronous rectifiers would be activated later on by the compensator. */
    pwm_sync_stop();
    /* Enable burst counter clock */
    XMC_CCU4_EnableClock(BURST_CNT.ccu4_handle->kernel_ptr,BURST_CNT.ccu4_handle->slice_number);
}
/*---------------------------------------------------------------------------*/
/**
 * \brief		Default dead times adjustment
 * \return 		None
 *
 * Dead time independent of the current.
 */
void pwm_dt_set()
{
	current_ctr.dt_A_B = parameters_RAM.dead_time_A_B;
	current_ctr.dt_C_D = parameters_RAM.dead_time_C_D - PWM_C_D_CHAIN_DLY;
    PWM_A_B.hrc_slice_ptr->SDCR = current_ctr.dt_A_B;
    PWM_A_B.hrc_slice_ptr->SDCF = current_ctr.dt_A_B;
    PWM_C.hrc_slice_ptr->SDCR = current_ctr.dt_C_D;
    PWM_D.hrc_slice_ptr->SDCR = current_ctr.dt_C_D;
    XMC_HRPWM_EnableHighResolutionShadowTransfer(PWM_C.hrc_module_ptr, PWM_A_B.hr_dt_transfer_msk | PWM_C.hr_dt_transfer_msk | PWM_D.hr_dt_transfer_msk);
}
/**}@*/
